﻿// Inner Fire 游戏引擎库
// 场景分装 - 基本几何体
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 这个类分装各种场景的构建（都在一个文件里面太大了）
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-11-25

#include <dx_graphics.h>

namespace ifire {

/// <summary>
/// 创建立方体
/// </summary>
void DxGraphics::BuildMesh_Box() {
  GeometryGenerator geoGen;
  GeometryGenerator::MeshData box = geoGen.CreateBox(8.0f, 8.0f, 8.0f, 3);

  std::vector<Vertex> vertices(box.Vertices.size());
  for (size_t i = 0; i < box.Vertices.size(); ++i) {
    auto& p = box.Vertices[i].Position;
    vertices[i].Pos = p;
    vertices[i].Normal = box.Vertices[i].Normal;
    vertices[i].TexC = box.Vertices[i].TexC;
  }

  const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);

  std::vector<std::uint16_t> indices = box.GetIndices16();
  const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

  auto geo = std::make_unique<SimpleMesh>();
  geo->Name = "BoxGeo";

  HASSERT(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
  CopyMemory(
      geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

  HASSERT(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
  CopyMemory(
      geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

  geo->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vbByteSize, geo->VertexBufferUploader);

  geo->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), ibByteSize, geo->IndexBufferUploader);

  geo->VertexByteStride = sizeof(Vertex);
  geo->VertexBufferByteSize = vbByteSize;
  geo->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo->IndexBufferByteSize = ibByteSize;

  SimpleSubmesh submesh;
  submesh.IndexCount = (UINT)indices.size();
  submesh.StartIndexLocation = 0;
  submesh.BaseVertexLocation = 0;

  geo->DrawArgs["box"] = submesh;

  geometries_["BoxGeo"] = std::move(geo);
}

/// <summary>
/// 创建立方体，GRID网格，球体，圆柱体
/// </summary>
void DxGraphics::BuildMesh_Shapes() {
  GeometryGenerator generator;
  using GMesh = GeometryGenerator::MeshData;

  GMesh box = generator.CreateBox(1.5f, 0.5f, 1.5f, 3);
  GMesh grid = generator.CreateGrid(20.0f, 30.0f, 60, 40);
  GMesh sphere = generator.CreateSphere(0.5f, 20, 20);
  GMesh cylinder = generator.CreateCylinder(0.5f, 0.3f, 3.0f, 20, 20);

  // 将所有的几何体数据都合并到一对大的顶点/索引缓冲区中
  // 以此来定义每个子网格数据在缓冲区中所点的范围

  // 对合并顶点缓冲区中每个物体的顶点偏移量进行缓存
  UINT box_vertex_offset = 0;
  UINT grid_vertex_offset = (UINT)box.Vertices.size();
  UINT sphere_vertex_offset = grid_vertex_offset + (UINT)grid.Vertices.size();
  UINT cylinder_vertex_offset =
      sphere_vertex_offset + (UINT)sphere.Vertices.size();

  // 索引偏移量
  UINT box_index_offset = 0;
  UINT grid_index_offset = (UINT)box.Indices32.size();
  UINT sphere_index_offset = grid_index_offset + (UINT)grid.Indices32.size();
  UINT cylinder_index_offset =
      sphere_index_offset + (UINT)sphere.Indices32.size();

  // 定义的多个Submesh，包括缓冲区中不同的几何体
  SimpleSubmesh box_submesh;
  box_submesh.IndexCount = (UINT)box.Indices32.size();
  box_submesh.StartIndexLocation = box_index_offset;
  box_submesh.BaseVertexLocation = box_vertex_offset;

  SimpleSubmesh grid_submesh;
  grid_submesh.IndexCount = (UINT)grid.Indices32.size();
  grid_submesh.StartIndexLocation = grid_index_offset;
  grid_submesh.BaseVertexLocation = grid_vertex_offset;

  SimpleSubmesh sphere_submesh;
  sphere_submesh.IndexCount = (UINT)sphere.Indices32.size();
  sphere_submesh.StartIndexLocation = sphere_index_offset;
  sphere_submesh.BaseVertexLocation = sphere_vertex_offset;

  SimpleSubmesh cylinder_submesh;
  cylinder_submesh.IndexCount = (UINT)cylinder.Indices32.size();
  cylinder_submesh.StartIndexLocation = cylinder_index_offset;
  cylinder_submesh.BaseVertexLocation = cylinder_vertex_offset;

  // 提取所需要的顶点元素，再将所有网格的顶点装进一个顶点缓冲区
  auto total_vertex_count =    //
      box.Vertices.size() +    //
      grid.Vertices.size() +   //
      sphere.Vertices.size() + //
      cylinder.Vertices.size();
  std::vector<Vertex> vertices(total_vertex_count);

  // 依次填充
  UINT k = 0;
  for (size_t i = 0; i < box.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = box.Vertices[i].Position;
  }
  for (size_t i = 0; i < grid.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = grid.Vertices[i].Position;
  }
  for (size_t i = 0; i < sphere.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = sphere.Vertices[i].Position;
  }
  for (size_t i = 0; i < cylinder.Vertices.size(); ++i, ++k) {
    vertices[k].Pos = cylinder.Vertices[i].Position;
  }

  // 填充索引
  std::vector<std::uint16_t> indices;
  indices.insert(indices.end(), std::begin(box.GetIndices16()),
      std::end(box.GetIndices16()));
  indices.insert(indices.end(), std::begin(grid.GetIndices16()),
      std::end(grid.GetIndices16()));
  indices.insert(indices.end(), std::begin(sphere.GetIndices16()),
      std::end(sphere.GetIndices16()));
  indices.insert(indices.end(), std::begin(cylinder.GetIndices16()),
      std::end(cylinder.GetIndices16()));

  // 创建常量缓冲区
  const UINT vertex_byte_size = (UINT)vertices.size() * sizeof(Vertex);
  const UINT index_byte_size = (UINT)indices.size() * sizeof(std::uint16_t);

  // 创建渲染的对象
  geo_ = std::make_unique<SimpleMesh>();
  geo_->Name = "shapeGeo";

  // 将数据上传到CPU缓冲区
  HASSERT(D3DCreateBlob(vertex_byte_size, &geo_->VertexBufferCPU));
  CopyMemory(geo_->VertexBufferCPU->GetBufferPointer(), vertices.data(),
      vertex_byte_size);
  HASSERT(D3DCreateBlob(index_byte_size, &geo_->IndexBufferCPU));
  CopyMemory(geo_->IndexBufferCPU->GetBufferPointer(), indices.data(),
      index_byte_size);

  // 创建默认缓冲区
  geo_->VertexBufferGPU = CreateDefaultBuffer(
      vertices.data(), vertex_byte_size, geo_->VertexBufferUploader);
  geo_->IndexBufferGPU = CreateDefaultBuffer(
      indices.data(), index_byte_size, geo_->IndexBufferUploader);

  geo_->VertexByteStride = sizeof(Vertex);
  geo_->VertexBufferByteSize = vertex_byte_size;
  geo_->IndexFormat = DXGI_FORMAT_R16_UINT;
  geo_->IndexBufferByteSize = index_byte_size;

  geo_->DrawArgs["box"] = box_submesh;
  geo_->DrawArgs["grid"] = grid_submesh;
  geo_->DrawArgs["sphere"] = sphere_submesh;
  geo_->DrawArgs["cylinder"] = cylinder_submesh;

  geometries_[geo_->Name] = std::move(geo_);
}

///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////

void DxGraphics::BuildRenderItems_Shapes() {
  auto geo = geometries_["shapeGeo"].get();

  // 用于通用的设置部分
  auto set_object = [geo](RenderItem* item, int buffer_index,
                        const char* obj_name) {
    item->ObjCBIndex = buffer_index;
    item->Geo = geo;
    item->PrimitiveType = D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    auto& mesh = geo->DrawArgs[obj_name];
    item->IndexCount = mesh.IndexCount;
    item->StartIndexLocation = mesh.StartIndexLocation;
    item->BaseVertexLocation = mesh.BaseVertexLocation;
  };

  // 立方体
  auto box_item = std::make_unique<RenderItem>();
  XMStoreFloat4x4(&box_item->World, XMMatrixScaling(2.0f, 2.0f, 2.0f) *
                                        XMMatrixTranslation(0.0f, 0.5f, 0.0f));
  set_object(box_item.get(), 0, "box");
  items_.push_back(std::move(box_item));

  // 地面
  auto grid_item = std::make_unique<RenderItem>();
  grid_item->World = IDENTITY_4X4;
  set_object(grid_item.get(), 1, "grid");
  items_.push_back(std::move(grid_item));

  // 构建柱体和球体
  UINT object_buffer_index = 2; // 前面有两个了
  for (int i = 0; i < 5; i++) {
    auto left_cylinder = std::make_unique<RenderItem>();
    auto right_cylinder = std::make_unique<RenderItem>();
    auto left_shpere = std::make_unique<RenderItem>();
    auto right_shpere = std::make_unique<RenderItem>();

    XMMATRIX left_cylinder_world =
        XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i * 5.0f);
    XMMATRIX right_cylinder_world =
        XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i * 5.0f);

    XMMATRIX left_shpere_world =
        XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i * 5.0f);
    XMMATRIX right_shpere_world =
        XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i * 5.0f);

    XMStoreFloat4x4(&left_cylinder->World, left_cylinder_world);
    set_object(left_cylinder.get(), object_buffer_index++, "cylinder");

    XMStoreFloat4x4(&right_cylinder->World, right_cylinder_world);
    set_object(right_cylinder.get(), object_buffer_index++, "cylinder");

    XMStoreFloat4x4(&left_shpere->World, left_shpere_world);
    set_object(left_shpere.get(), object_buffer_index++, "sphere");

    XMStoreFloat4x4(&right_shpere->World, right_shpere_world);
    set_object(right_shpere.get(), object_buffer_index++, "sphere");

    items_.push_back(std::move(left_cylinder));
    items_.push_back(std::move(right_cylinder));
    items_.push_back(std::move(left_shpere));
    items_.push_back(std::move(right_shpere));
  }

  // 所有项目都不透明
  for (auto& e : items_) {
    opaque_items_.push_back(e.get());
  }
}

} // namespace ifire